Skip to content

fix(bundler): resolve cnpmcore pack blockers#5911

Merged
killagu merged 5 commits intonextfrom
agent/egg-dev/a5ecaa1b
May 2, 2026
Merged

fix(bundler): resolve cnpmcore pack blockers#5911
killagu merged 5 commits intonextfrom
agent/egg-dev/a5ecaa1b

Conversation

@killagu
Copy link
Copy Markdown
Contributor

@killagu killagu commented May 2, 2026

Summary

Fixes the current cnpmcore production bundle blockers seen on next after #5910:

  • generate worker entries with a portable framework import instead of an absolute node_modules/egg path
  • inline the bundle module loader registration so the generated worker does not depend on @eggjs/utils
  • provide runtime exports for EggAppConfig and Logger so non-import type app code survives static export validation
  • externalize packages that have missing optional peer dependencies, and add the missing optional peers as externals, covering leoric optional sql.js / mysql / sqlite3 paths

Validation

  • pnpm vitest run test/EntryGenerator.test.ts test/ExternalsResolver.test.ts in tools/egg-bundler
  • pnpm vitest run packages/egg/test/index.test.ts
  • pnpm --filter egg run typecheck
  • pnpm --filter @eggjs/egg-bundler run typecheck
  • full pnpm run build was run during verification after the implementation changes
  • cnpmcore validation with local egg CLI/dist shim: node ../egg/tools/egg-bin/bin/run.js bundle --mode production --output dist-bundle generated dist-bundle successfully

Summary by CodeRabbit

  • New Features

    • Bundler now externalizes missing optional peer dependencies for more correct builds.
  • Bug Fixes

    • Configuration and logger exports are exposed at runtime so apps start reliably.
  • Chores

    • Removed an unused dependency.
    • Standardized the bundler’s runtime module-loader approach.
  • Documentation

    • Updated bundling docs to reflect new module-loader and startup behavior.
  • Tests

    • Added/updated tests covering externals resolution and generated runtime hooks.

Copilot AI review requested due to automatic review settings May 2, 2026 14:45
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 2, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds runtime exports for EggAppConfig/Config and a runtime Logger re-export; updates tests to assert those runtime exports; removes @eggjs/utils from the bundler; replaces setBundleModuleLoader with globalThis.__EGG_BUNDLE_MODULE_LOADER__; improves framework import spec resolution for absolute framework dirs; externalizes missing optional peerDependencies.

Changes

Runtime exports and tests

Layer / File(s) Summary
Runtime value shim
packages/egg/src/lib/types.ts
Added export const EggAppConfig: ObjectConstructor = Object; to provide a runtime value alongside the existing EggAppConfig interface.
Module exports
packages/egg/src/index.ts
Replaced type-only Config re-export with export const Config: typeof EggAppConfig = EggAppConfig; plus export type Config = EggAppConfigType;. Split logger exports into a runtime export { EggLogger as Logger } and type-only export type { LoggerLevel, EggLogger }.
Tests
packages/egg/test/index.test.ts
Extended assertions to check egg.Context is defined, egg.Config === Object, egg.EggAppConfig === Object, and egg.Logger is present, in addition to the snapshot.

Bundler: module-loader, framework specifier, and externals

Layer / File(s) Summary
Dependency manifest
tools/egg-bundler/package.json
Removed the @eggjs/utils dependency.
Entry template wiring
tools/egg-bundler/src/lib/EntryGenerator.ts
Worker entry generation now uses #toFrameworkImportSpecifier() for frameworkSpec; removed import { setBundleModuleLoader } from '@eggjs/utils'; assigns globalThis.__EGG_BUNDLE_MODULE_LOADER__ to a loader function that normalizes paths and returns from __BUNDLE_MAP; added helpers #toFrameworkImportSpecifier(), #packageNameFromDir(), #canUseFrameworkPackageName(), #isInsideDir(), #samePath().
Externals resolution logic
tools/egg-bundler/src/lib/ExternalsResolver.ts
Added peerDependenciesMeta support; added #addMissingOptionalPeerExternals() to add missing optional peers to externals; updated #shouldExternalize() to detect missing optional peers via #hasMissingOptionalPeerDependencies(); refactored #findPackageDir(fromDir, name) with cache keyed by ${fromDir}\0${name} for relative lookups.
Tests, fixtures & docs
tools/egg-bundler/test/*, tools/egg-bundler/test/fixtures/externals/*, tools/egg-bundler/docs/output-structure.md
Updated tests to expect __EGG_BUNDLE_MODULE_LOADER__ and removed setBundleModuleLoader expectations; added tests for framework import spec resolution when framework is an absolute dir; added tests for optional-peer externalization and user override behavior; updated fixture deps (optional-peer-host, normal-js); docs updated to describe global hook usage and bundle behavior.

Sequence Diagram(s)

sequenceDiagram
    participant Worker as Generated Worker Entry
    participant Global as globalThis
    participant BundleMap as __BUNDLE_MAP
    participant Runtime as Module Importer

    Worker->>Global: set __EGG_BUNDLE_MODULE_LOADER__ = (filepath) => __BUNDLE_MAP[normalizedKey]
    Worker->>Worker: call startEgg({ baseDir, mode: 'single' })
    Runtime->>Global: invoke __EGG_BUNDLE_MODULE_LOADER__(filepath)
    Global->>BundleMap: normalize filepath -> lookup module
    BundleMap-->>Runtime: return module
    Runtime-->>Worker: module resolved from bundle
Loading
sequenceDiagram
    participant Resolver as ExternalsResolver
    participant RootPkg as Root package deps
    participant Pkg as Installed dependency
    participant NodeMods as node_modules FS
    participant Result as externals map

    Resolver->>RootPkg: iterate dependencies
    loop per dependency
        Resolver->>Pkg: read package.json (peerDependencies, peerDependenciesMeta)
        Resolver->>NodeMods: find peer package dir relative to Pkg (fromDir)
        alt missing optional peers
            Resolver->>Result: add missing optional peer(s) to externals
        else
            Resolver->>Resolver: evaluate native-binary and other rules
        end
    end
    Resolver-->>Result: return final externals map
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • fengmk2
  • jerryliang64

"🐰
I nudged Config into runtime light,
Rewired the loader to global sight.
Logger hopped up — now visible too;
Optional peers I sniffed anew.
Hooray — the bundle’s tidy and bright! 🥕"

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title 'fix(bundler): resolve cnpmcore pack blockers' accurately describes the main objective of the PR, which is to fix production bundle blockers for cnpmcore by addressing framework imports, module loader inlining, runtime exports, and optional peer dependency externalization.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/egg-dev/a5ecaa1b

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 2, 2026

Deploying egg with  Cloudflare Pages  Cloudflare Pages

Latest commit: a054390
Status: ✅  Deploy successful!
Preview URL: https://19abf11c.egg-cci.pages.dev
Branch Preview URL: https://agent-egg-dev-a5ecaa1b.egg-cci.pages.dev

View logs

@codecov
Copy link
Copy Markdown

codecov Bot commented May 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.03%. Comparing base (fef9325) to head (a054390).
⚠️ Report is 2 commits behind head on next.

Additional details and impacted files
@@            Coverage Diff             @@
##             next    #5911      +/-   ##
==========================================
- Coverage   85.04%   85.03%   -0.01%     
==========================================
  Files         665      667       +2     
  Lines       19108    19110       +2     
  Branches     3719     3719              
==========================================
+ Hits        16250    16251       +1     
- Misses       2465     2466       +1     
  Partials      393      393              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces several improvements to the egg package and the egg-bundler tool. Key changes include exposing EggAppConfig at runtime to support decorator metadata, refining the export of Logger, and removing the dependency on @eggjs/utils in the bundler by using a global loader. Additionally, the egg-bundler now correctly handles missing optional peer dependencies by externalizing them and can resolve package names from absolute framework paths. I have no feedback to provide.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 2, 2026

Deploying egg-v3 with  Cloudflare Pages  Cloudflare Pages

Latest commit: a054390
Status: ✅  Deploy successful!
Preview URL: https://7ab8df0b.egg-v3.pages.dev
Branch Preview URL: https://agent-egg-dev-a5ecaa1b.egg-v3.pages.dev

View logs

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes several production-bundle blockers for cnpmcore by making the generated bundle entry more portable, reducing runtime coupling, improving bundler externals detection for optional peers, and ensuring certain framework symbols exist at runtime.

Changes:

  • Update EntryGenerator to (a) avoid absolute framework import paths by preferring the framework package name and (b) register the bundle module loader via globalThis.__EGG_BUNDLE_MODULE_LOADER__ (dropping the @eggjs/utils dependency).
  • Extend ExternalsResolver to externalize packages that have missing optional peerDependencies and to add those missing optional peers to externals.
  • Add runtime exports for EggAppConfig and Logger from egg so non-import type usage survives bundler/static-export checks, with corresponding test/snapshot updates.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tools/egg-bundler/src/lib/EntryGenerator.ts Makes worker entry portable (framework import) and inlines bundle-loader registration via globalThis.
tools/egg-bundler/package.json Drops @eggjs/utils dependency now that worker output no longer imports it.
tools/egg-bundler/src/lib/ExternalsResolver.ts Adds detection for missing optional peerDependencies and externalizes missing optional peers.
tools/egg-bundler/test/ExternalsResolver.test.ts Adds coverage for missing optional peerDependency externalization and inline override behavior.
tools/egg-bundler/test/fixtures/externals/basic-app/package.json Adds optional-peer-host to fixture dependencies.
tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/optional-peer-host/package.json New fixture package declaring optional/required peers for resolver tests.
tools/egg-bundler/test/EntryGenerator.test.ts Updates assertions to match __EGG_BUNDLE_MODULE_LOADER__ registration.
tools/egg-bundler/test/snapshots/EntryGenerator.worker.canonical.snap Updates canonical worker snapshot for the new loader registration approach.
packages/egg/src/lib/types.ts Adds runtime EggAppConfig export (value) while keeping the interface as the type.
packages/egg/src/index.ts Exposes runtime Logger export (value) from egg-logger and keeps type exports.
packages/egg/test/index.test.ts Asserts runtime presence of EggAppConfig and Logger.
packages/egg/test/snapshots/index.test.ts.snap Snapshot update reflecting new runtime exports.

Comment thread tools/egg-bundler/src/lib/EntryGenerator.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tools/egg-bundler/src/lib/ExternalsResolver.ts (1)

53-60: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

#addMissingOptionalPeerExternals is silently skipped for force-listed packages.

When a package appears in both deps and force, the force loop populates result[name] first. The subsequent if (result[name]) continue at line 55 then short-circuits before reaching line 59, so the optional peer propagation for that package never runs.

Concrete scenario: force: ['optional-peer-host']missing-optional-peer is never added to externals, contrary to the automatic behavior when the same package is a plain dep.

Moving the call up resolves this:

🐛 Proposed fix
     for (const name of deps) {
       if (this.#inline.has(name) && !this.#force.has(name)) continue;
+      await this.#addMissingOptionalPeerExternals(name, result);
       if (result[name]) continue;
       if (await this.#shouldExternalize(name, optionalDeps, peerDeps)) {
         result[name] = name;
       }
-      await this.#addMissingOptionalPeerExternals(name, result);
     }

#addMissingOptionalPeerExternals already guards with if (result[peerName]) continue, so calling it earlier is safe.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tools/egg-bundler/src/lib/ExternalsResolver.ts` around lines 53 - 60, The
loop over deps skips calling `#addMissingOptionalPeerExternals` for packages
already set in result (e.g., ones added by the force set), so optional peers of
force-listed packages never get propagated; to fix, move the await
this.#addMissingOptionalPeerExternals(name, result) call before the if
(result[name]) continue check (or right after the inline/force skip check) so
optional peer propagation runs even for force-listed
packages—#addMissingOptionalPeerExternals already guards against duplicate
peers, so this is safe; locate the loop using deps, this.#inline, this.#force,
result and `#shouldExternalize` to apply the change.
🧹 Nitpick comments (1)
packages/egg/test/index.test.ts (1)

8-9: ⚡ Quick win

Use assert for the new non-snapshot checks.

These added assertions extend the public API contract, so it would be better to express them with the repo's standard assertion style and keep expect only for the snapshot on Line 6.

Suggested update
+import assert from 'node:assert/strict';
 import { test, expect } from 'vitest';
 
 import * as egg from '../src/index.ts';
 
 test('should expose properties', () => {
   expect(egg).toMatchSnapshot();
-  expect(egg.Context).toBeDefined();
-  expect(egg.EggAppConfig).toBe(Object);
-  expect(egg.Logger).toBeDefined();
+  assert.ok(egg.Context);
+  assert.strictEqual(egg.EggAppConfig, Object);
+  assert.ok(egg.Logger);
 });

As per coding guidelines, packages/**/test/**/*.test.ts: Use Node.js built-in 'assert' module for assertions in tests.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/egg/test/index.test.ts` around lines 8 - 9, Replace the two new Jest
expect checks with Node's assert-based assertions: import the built-in 'assert'
and use assert.strictEqual(egg.EggAppConfig, Object) to mirror
expect(...).toBe(Object) and use assert.ok(egg.Logger) (or
assert.notStrictEqual(egg.Logger, undefined)) to mirror
expect(...).toBeDefined(); keep the existing expect snapshot assertion on line 6
unchanged. Reference the symbols egg.EggAppConfig and egg.Logger and update the
test file's imports accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/egg/src/index.ts`:
- Around line 54-55: The runtime alias for Config is missing which causes
runtime import failures; add a runtime export for EggAppConfig named Config
(i.e., export { EggAppConfig as Config } from './lib/types.ts') placed before
the existing type-only export so that importing { Config } works at runtime just
like the existing export { EggLogger as Logger } ensures Logger is available at
runtime; update the exports in packages/egg/src/index.ts to include this runtime
export alongside the current type export for Config.

---

Outside diff comments:
In `@tools/egg-bundler/src/lib/ExternalsResolver.ts`:
- Around line 53-60: The loop over deps skips calling
`#addMissingOptionalPeerExternals` for packages already set in result (e.g., ones
added by the force set), so optional peers of force-listed packages never get
propagated; to fix, move the await this.#addMissingOptionalPeerExternals(name,
result) call before the if (result[name]) continue check (or right after the
inline/force skip check) so optional peer propagation runs even for force-listed
packages—#addMissingOptionalPeerExternals already guards against duplicate
peers, so this is safe; locate the loop using deps, this.#inline, this.#force,
result and `#shouldExternalize` to apply the change.

---

Nitpick comments:
In `@packages/egg/test/index.test.ts`:
- Around line 8-9: Replace the two new Jest expect checks with Node's
assert-based assertions: import the built-in 'assert' and use
assert.strictEqual(egg.EggAppConfig, Object) to mirror expect(...).toBe(Object)
and use assert.ok(egg.Logger) (or assert.notStrictEqual(egg.Logger, undefined))
to mirror expect(...).toBeDefined(); keep the existing expect snapshot assertion
on line 6 unchanged. Reference the symbols egg.EggAppConfig and egg.Logger and
update the test file's imports accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 489eafa0-39d0-4003-a516-f7e01f577759

📥 Commits

Reviewing files that changed from the base of the PR and between fef9325 and 312a42e.

⛔ Files ignored due to path filters (3)
  • packages/egg/test/__snapshots__/index.test.ts.snap is excluded by !**/*.snap
  • tools/egg-bundler/test/__snapshots__/EntryGenerator.worker.canonical.snap is excluded by !**/*.snap
  • tools/egg-bundler/test/fixtures/externals/basic-app/node_modules/optional-peer-host/package.json is excluded by !**/node_modules/**
📒 Files selected for processing (9)
  • packages/egg/src/index.ts
  • packages/egg/src/lib/types.ts
  • packages/egg/test/index.test.ts
  • tools/egg-bundler/package.json
  • tools/egg-bundler/src/lib/EntryGenerator.ts
  • tools/egg-bundler/src/lib/ExternalsResolver.ts
  • tools/egg-bundler/test/EntryGenerator.test.ts
  • tools/egg-bundler/test/ExternalsResolver.test.ts
  • tools/egg-bundler/test/fixtures/externals/basic-app/package.json
💤 Files with no reviewable changes (1)
  • tools/egg-bundler/package.json

Comment thread packages/egg/src/index.ts
@killagu
Copy link
Copy Markdown
Contributor Author

killagu commented May 2, 2026

Addressed the remaining non-inline CodeRabbit feedback in 5167978: optional peer propagation now also runs for force-listed packages, and the new egg export assertions use node:assert.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/egg/src/index.ts`:
- Line 20: The exported runtime alias Config currently lacks an explicit type,
causing TS9010 under --isolatedDeclarations; update the declaration "export
const Config = EggAppConfig;" to include an explicit type annotation (use
"export const Config: typeof EggAppConfig = EggAppConfig;") so the emitted
declaration can reference the symbol correctly while leaving the separate type
export (the existing type export for EggAppConfig) unchanged.

In `@tools/egg-bundler/docs/output-structure.md`:
- Around line 33-37: The sentence overstated behavior—change the doc text around
ManifestStore.setBundleStore(...) and globalThis.__EGG_BUNDLE_MODULE_LOADER__ so
it no longer claims absolute "no fs.readdir scanning at runtime"; instead state
that installing the bundle loader before startEgg({ baseDir, mode: 'single' })
makes framework module resolution use the inlined bundle map (avoiding
fs.readdir for bundled framework files), but note that application code or
plugins may still perform fs operations (e.g., loading config, views, assets),
so runtime fs access is reduced but not completely eliminated.

In `@tools/egg-bundler/src/lib/ExternalsResolver.ts`:
- Around line 76-81: The peer-resolution loop uses `#findPackageDir`(peerName)
which always starts lookup from this.#baseDir and misses peers resolved from the
dependent package's node_modules; update calls in the loop (where peerName is
iterated) and the other call at the later check to pass the dependent package
directory (pkgDir) as the lookup anchor (e.g., this.#findPackageDir(peerName,
pkgDir)), and modify `#findPackageDir/`#findPackageDirUncached to accept an
optional startDir parameter and use that startDir instead of always using
this.#baseDir so peer lookups correctly resolve from the dependent package's
directory.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cc54f013-953c-4f2b-b53d-258e789009bf

📥 Commits

Reviewing files that changed from the base of the PR and between 312a42e and 5167978.

⛔ Files ignored due to path filters (1)
  • packages/egg/test/__snapshots__/index.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (6)
  • packages/egg/src/index.ts
  • packages/egg/test/index.test.ts
  • tools/egg-bundler/docs/output-structure.md
  • tools/egg-bundler/src/lib/ExternalsResolver.ts
  • tools/egg-bundler/test/ExternalsResolver.test.ts
  • tools/egg-bundler/test/integration.test.ts
✅ Files skipped from review due to trivial changes (1)
  • tools/egg-bundler/test/integration.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/egg/test/index.test.ts
  • tools/egg-bundler/test/ExternalsResolver.test.ts

Comment thread packages/egg/src/index.ts Outdated
Comment thread tools/egg-bundler/docs/output-structure.md Outdated
Comment thread tools/egg-bundler/src/lib/ExternalsResolver.ts
Copilot AI review requested due to automatic review settings May 2, 2026 15:21
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 1 comment.

Comment thread tools/egg-bundler/src/lib/EntryGenerator.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants